<?php

namespace app\admin\command;

use app\admin\model\Clss;
use app\admin\model\Match;
use app\admin\model\Member;
use app\admin\model\Project;
use app\admin\model\SportRecordNew as SportRecord;
use app\admin\model\Standard;
use app\admin\model\Task;
use app\admin\service\BaseService;
use app\admin\service\SportService;
use think\console\Command;
use think\console\Input;
use think\console\input\Argument;
use think\console\input\Option;
use think\console\Output;
use think\Db;
use think\facade\Cache;


class PullSportRecord extends Command
{

    protected function configure()
    {
        $this->setName('pull-sport-record')
            ->addArgument('method', Argument::OPTIONAL, '拉取方式 def(默认): 上次至现在,或者配合-c使用; all: 全量; custom: 自定义')
            ->addOption('custom', 'c', Option::VALUE_OPTIONAL, '自定义日期')
            ->setDescription('拉取运动数据');
    }

    protected function execute(Input $input, Output $output)
    {
        error_reporting(E_ALL);
        ini_set('display_errors', 1);
//        $this->calcBmi();
//        return;
        $method = trim($input->getArgument('method')) ?: 'def';
        if ($method == 'def') {
            $endTime   = time() * 1000;
            $startTime = Cache::get('lastPullSportRecordTime', (strtotime('today')) * 1000);
            $total = 0;
            foreach ([1, 2] as $type) {
                $total += $this->pullSportRecord($type, $startTime, $endTime);
            }
            Cache::set('lastPullSportRecordTime', $endTime, 86400);
            $output->writeln(date('Y-m-d [H:i:s - ', $startTime/1000) . date('H:i:s]', $endTime/1000) . ': ' . $total . '条');
        } elseif ($method == 'custom') {
            $custom = $input->getOption('custom');
            if (empty($custom)) {
                $output->error('自定义日期缺失');
                return;
            }
            if (substr($custom, 0, 1) == 'g') {
                $startTime = strtotime(substr($custom, 1));
                do {
                    $total = 0;
                    foreach ([1, 2] as $type) {
                        $total += $this->pullSportRecord($type, $startTime * 1000, ($startTime + 86399) * 1000);
                    }
                    $output->writeln(date('Y-m-d', $startTime) . ': ' . $total . '条');
                    sleep(5);
                    $startTime += 86400;
                } while ($startTime < time());
            } else {
                $startTime = strtotime($custom);
                $endTime   = $startTime + 86399;
                $total     = 0;
                foreach ([1, 2] as $type) {
                    $total += $this->pullSportRecord($type, $startTime * 1000, $endTime * 1000);
                }
                $output->writeln($custom . ': ' . $total . '条');
            }
        } elseif ($method == 'all') {
            // 查询所有旧表的日期加上之后的每天
            $dates = Db::name('sport_record')
                ->distinct(true)
                ->field('LEFT(createdDate, 10) as createdDate')
                ->whereNotNull('createdDate')
                ->order('createdDate')
                ->select();
            $dates = array_column($dates, 'createdDate');
            foreach ($dates as $date) {
                $total = 0;
                $begin = strtotime($date) * 1000;
                $end   = (strtotime($date) + 86399) * 1000;
                foreach ([1, 2] as $type) {
                    try {
                        $total += $this->pullSportRecord($type, $begin, $end);
                    } catch (\Throwable $e) {
                        $output->error($date . ': error ' . $e->getMessage());
                    }
                }
                $output->writeln($date . ': ' . $total . '条');
                sleep(5);
            }
        } else {
            $output->error('错误的拉取方式:' . $method);

            return;
        }
        $output->writeln('数据拉取完成，开始计算BMI');

        $this->calcBmi();
    }

    private function pullSportRecord($type, $startTime, $endTime): int
    {
        $total        = 0;
        $pageSize     = 1000;
        $page         = 1;
        $sportService = new SportService;

        $projectTemp = (new Project)
            ->field(['organization', 'code', 'id', 'type'])
            ->where('mark', 1)
            ->select()
            ->toArray();
        $project     = [];
        foreach ($projectTemp as $val) {
            $project[$val['organization']][$val['code']] = $val;
        }

        $standardTemp  = (new Standard)
            ->field(['standard', 'organization', 'project', 'id'])
            ->where('mark', 1)
            ->select()
            ->toArray();
        $standardData  = [];
        $standardData2 = [];
        foreach ($standardTemp as $val) {
            $standardData[$val['organization']][$val['project']] = $val['standard'];
            $standardData2[$val['id']]                           = $val['standard'];
        }
        do {
            synchronousResult:
            $result = (new BaseService)->synchronousResult(
                [
                    'type'           => $type,
                    'startTimestamp' => $startTime,
                    'endTimestamp'   => $endTime,
                    'pageNo'         => $page,
                    'pageSize'       => $pageSize
                ]
            );
            if (isset($result['code']) && $result['code'] == '1009') {
                $this->output->writeln($result['message'] . ' wait 5s');
                sleep(5);
                goto synchronousResult;
            }
            $rows = $result['result']['rows'] ?? [];
            if (empty($rows)) {
                break;
            }
            $ids           = [];
            $testerNumbers = [];
            $minStartTime  = time();
            $maxEndTime    = 0;
            foreach ($rows as $row) {
                $ids[]           = $row['id'];
                $testerNumbers[] = $row['testerNumber'];
                $minStartTime    = min($minStartTime, strtotime($row['startTime']));
                $maxEndTime      = max($maxEndTime, strtotime($row['endTime']));
            }
            $delRecords = Db::name('sportrecord_del')->whereIn('tbid', $ids)->column('tbid');
            $exists     = (new SportRecord)->whereIn('id', $ids)->column('id');

            $members = (new Member)->whereIn('roll', $testerNumbers)->where('mark', 1)->select()->toArray();
            $members = array_column($members, null, 'roll');

            $taskDataTemp = (new Task)
//                ->where('task_start', '<=', date('Y-m-d H:i:s', $minStartTime))
//                ->where('task_end', '>=', date('Y-m-d H:i:s', $maxEndTime))
                ->where('status', 1)
                ->where('mark', 1)
                ->select()
                ->toArray();
            $taskData     = [];
            foreach ($taskDataTemp as $v) {
                $taskData[$v['project']][] = $v;
            }

            $matchDataTemp = (new Match)
//                ->where('begin_time', '<=', $minStartTime)
//                ->where('end_time', '>=', $maxEndTime)
                ->where('mark', 1)
                ->select()
                ->toArray();
            $matchData     = [];
            foreach ($matchDataTemp as $v) {
                $matchData[$v['projectId']][] = $v;
            }

            $update = [];
            $insert = [];
            foreach ($rows as &$row) {
                if (in_array($row['id'], $delRecords)) {
                    continue;
                }
                $member = $members[$row['testerNumber']] ?? null;
                if (!$member) {
                    continue;
                }
                $row['type']              = $type;
                $row['tbid']              = $row['id'];
                $row['memberId']          = $member['id'];
                $row['schoolId']          = $member['organization'];
                $row['gradeId']           = $member['grade'];
                $row['classId']           = $member['class'];
                $row['departmentsId']     = $member['superior'];
                $row['tiyu_superior']     = $member['tiyu_superior'];
                $row['professionalId']    = $member['professional'];
                $row['tiyu_professional'] = $member['tiyu_professional'];
                $row['tiyu_class']        = $member['features'] ? intval(json_decode($member['features'], true)[0]) : 0;
                $row['tiyu_class_string'] = $member['features'];
                if ($row['itemCode'] == 'weight' || $row['itemCode'] == 'height') {
                    $row['projectId'] = $project[$row['schoolId']]['BMI']['id'] ?? '';
                } else {
                    $row['projectId'] = $project[$row['schoolId']][$row['itemCode']]['id'] ?? '';
                }
                $row['stype']        = $project[$row['schoolId']][$row['itemCode']]['type'] ?? '';
                $row['gender']       = $row['gender'] == 'M' ? 1 : 2;
                $row['videoUrlList'] = isset($row['videoUrlList']) ? json_encode($row['videoUrlList']) : '';
                $row['taskId']       = 0;
                $row['model']        = '';

                $standard = '';
                if ($type == 1) {//体测数据
                    $task = null;
                    foreach ($taskData[$row['projectId']] ?? [] as $taskVal) {
                        if (strtotime($taskVal['task_start']) <= strtotime($row['startTime'])
                            && strtotime($taskVal['task_end']) >= strtotime($row['endTime'])) {
                            $task = $taskVal;
                            break;
                        }
                    }
                    if ($task) {
                        if ($task['class']) {
                            $classArr = json_decode($task['class'], true);
                            foreach ($classArr as $classVal) {
                                $df = (new Clss)
                                    ->where('id', intval($classVal))
                                    ->where('features', 'like', '%"' . $row['memberId'] . '"%')
                                    ->cache(125)
                                    ->value('id');
                                if ($df) {
                                    $row['tiyu_class'] = intval($classVal);
                                    $row['taskId']     = $task['id']; //任务id
                                }
                            }
                        }
                        $row['model'] = $task['model']; //任务模式
                        $standard     = $standardData2[$task['standard']] ?? '';
                    } else {
                        $match = null;
                        foreach ($matchData[$row['projectId']] ?? [] as $matchVal) {
                            if ($matchVal['begin_time'] <= strtotime($row['startTime'])
                                && $matchVal['end_time'] >= strtotime($row['endTime'])) {
                                $match = $matchVal;
                                break;
                            }
                        }
                        if ($match) {
                            if ($match['mtype'] == 1) {
                                $row['type'] = 3; //体育赛事
                            }
                            if ($match['mtype'] == 2) {
                                $row['type'] = 4; //院系测试
                            }
                            $row['taskId'] = $match['id'];
                            $standard      = $standardData[$row['schoolId']][$match['projectId']] ?? '';
                        } else {
                            $row['type'] = 2; //体锻数据
                            $pid         = $project[$row['schoolId']][$row['itemCode']]['id'] ?? 0;
                            $standard    = $standardData[$row['schoolId']][$pid] ?? '';
                        }
                    }
                } elseif ($type == 2) {//体锻数据
                    $pid      = $project[$row['schoolId']][$row['itemCode']]['id'] ?? 0;
                    $standard = $standardData[$row['schoolId']][$pid] ?? '';
                }
                [$row['rating'], $row['pingscore']] = $sportService->standardCalc($standard, $row['score'], $row['gender']);
                if (!isset($row['reportUrl'])) {
                    $row['reportUrl'] = '';
                }
                if (in_array($row['id'], $exists)) {
                    $update[] = $row;
                } else {
                    $insert[] = $row;
                }
                $total++;
            }
            unset($row);
            if (!empty($insert)) {
                foreach ($insert as $insertVal) {
                    Db::name('sport_record')->insert($insertVal);
                }
            }
            if (!empty($update)) {
                (new SportRecord)->allowField(true)->saveAll($update);
            }
            $page++;
        } while (count($rows) === $pageSize);

        return $total;
    }

    private function calcBmi(): void
    {
        $sportService = new SportService;
        $standard     = (new Standard)->where(['name' => 'BMI'])->value('standard');
        $limit        = 100;
        do {
            $list = SportRecord::field('id,memberId,score,gender,pingscore,rating,createdDate,deviceUnicode')
                ->whereNotNull('score')
                ->whereNull('bmi')
                ->where('projectId', 39)
                ->where('update_user', 0)
                ->where('itemCode', 'weight')
                ->order('id asc')
                ->limit($limit)
                ->select();

            $passIds = [];
            foreach ($list as $v) {
                $createDate = strtotime($v['createdDate']);
                $createDate = [$createDate - 5, $createDate + 5];
                $createDate = array_map(function ($val) {return date('Y-m-d H:i:s', $val);}, $createDate);
                $heightRow = SportRecord::field('id, score')
                    ->where('itemCode', 'height')
                    ->where('memberId', $v['memberId'])
                    ->whereBetween('createdDate', $createDate)
                    ->whereNotNull('score')
                    ->order('id asc')
                    ->find();
                if (!$heightRow) {
                    $heightRow = SportRecord::field('id, score')
                        ->where('itemCode', 'height')
                        ->where('projectId', 39)
                        ->where('memberId', $v['memberId'])
                        ->where('deviceUnicode', $v['deviceUnicode'])
                        ->whereNotNull('score')
                        ->whereNull('bmi')
                        ->order('id asc')
                        ->find();
                    if (!$heightRow) {
                        $passIds[] = $v['id'];
                        continue;
                    }
                }
                $height = $heightRow['score'] / 100;
                $update = [
                    'bmi' => pro_float(round($v['score'] / $height / $height, 2))
                ];
                [$update['rating'], $update['pingscore']] = $sportService->standardCalc(
                    $standard,
                    $update['bmi'],
                    $v['gender']
                );
                SportRecord::whereIn('id', [$heightRow['id'], $v['id']])->update($update);
            }
            if ($passIds) {
                SportRecord::whereIn('id', $passIds)->update(['update_user' => 1]);
            }
        } while (count($list) === $limit);
    }

}
